Jak napisać C++ DLL dla VB?

Wiemy, że wykonywanie kodu VB jest wolniejsze (dużo) od wykonywania kodu samego C++. Choć VB korzysta z kompilatora C++ to jednak zbyt dużo jest w kodzie (i najwięcej w runtime DLL) pozostałości ze starego VB. Chociaż kompilujemy program do N-kodu to i tak typy zmiennych nie są typami z C++ ale z VB...

Na szczęście możemy importować funkcje z zewnętrznych bibliotek, nie tylko z VB (Active X DLL, OCX), ale także z C (Windows API) oraz C++. Różnica, którą powinniśmy zauważyć, między aplikacjami C a C++ jest taka, że aplikacje języka C nie wymagają żadnych runtime'ów w postaci DLL. Wystarczy im Windows API.

Znając (przynajmniej w stopniu początkującym) język C++ możemy stworzyć bibliotekę DLL którą można by używać w kodzie VB. Zadanie to jednak jest trudniejsze niż tworzenie ActiveX DLL'ów (m.in. dlatego, że mamy do czynienia z C++).

Chociaż mam zainstalowane obydwa konkurencyjne edytory RAD języka C++, to wybieram dla aplikacji narzędzie Borlanda, a Visual'a C++ używam do zastosowań poważnych (właśnie takich jak DLL'e). Z tego powodu, że eksportowanie funkcji w dwóch tych różnych środowiskach jest inne, to poświęcę ten artykuł jedynie VC++. Mam nadzieję, że nikt nie będzie na mnie zły, ponieważ piszę tutaj dużo o C++ a nie o VB. Myślę, że należy sobie poszerzać horyzonty, a na początek używać C++ jedynie jako podpory.

Architektura biblioteki DLL
Biblioteka DLL na pozór nie różni się od zwykłego pliku wykonywalnego. Posiada wszystkie jego części, takie jak:
data, idata, rdata, reloc, text.
Różnicą jest to, że taka biblioteka posiada punkt wejściowy, zwany DLLEntryPoint. W punkcie wyjściowym znajdują się informacje dotyczące funkcji eksportowanych.

Taka biblioteka może mieć swoje funkcje wewnętrzne i eksportowane. My możemy korzystać tylko z funkcji eksportowanych.

Nie będę się zagłębiał w strukturę takiej biblioteki, ponieważ każdy może sobie ją zobaczyć, korzystając z kreatora (takiego wizard'a).

Jedną z najważniejszych aspektów, które musimy przedyskutować to konwencja wywoływania. Można ją ustawić w menu Porject\Settings w zakładce C\C++, ustawiając ComboBox'a na Code Generation. W tedy pojawi się opcja (w postaci listy combo) "Calling Convention".
Tutaj nie ma kompromisu. VB może korzystać tylko z bibliotek gdzie zastosowano odpowiednie konwencje. Dla bibliotek 16-bitowych jest to konwencja Pascal'a, a dla 32-bitowych konwencja Standard Call (__stdcall).

Po wybraniu konwencji dla napisanego DLL'a, w którym eksportowane są jakieś funkcje należy go skompilować i na razie wrzucić do katalogu systemowego (C:\Windows\System\). Znając parametry (lepiej je sobie gdzieś zapisać) danych funkcji moglibyśmy już niby zacząć korzystać z biblioteki. Wpisując deklarację:

Declare [nazwa funkcji] Lib "[nazwa pliku biblioteki]" ([argumenty]) As [typ zmiennej zwrotu (return)]

Na pewno wyskoczy nam błąd: "Can't find DllEntryPoint [tutaj nazwa funkcji] in [tutaj nazwa biblioteki]." I możemy pomyśleć, że biblioteka jest niekompatybilna.

Problem tkwi w tym, że nazwa funkcji jest odmienna od użytej w kodzie biblioteki. Dodawane są do niej jakieś dziwne znaczki. Skąd to wiem? Plik dumpbin.exe potrafi wyświetlić funkcje eksportowane (jak i ich numery) każdej biblioteki DLL. W tej chwili staje się oczywiste, że należy skorzystać z Alias'a.

Program dumpbin znajduje się w katalogu BIN, tam gdzie zainstalowany jest VC++. Do jego zastosowania użyjemy konsoli, czyli 'Tryb MS-DOS'. 
Używamy go z parametrem /EXPORTS i dalej podając ścieżkę do pliku, najlepiej wynik zapisać do pliku, czyli wpiszemy (dla biblioteki dll.dll):

dumpbin.exe /exports c:\windows\system\dll.dll >wynik.txt

Teraz wszystko co "wydrukowałby" program dumpbin zostanie w pliku wynik.txt który potem będziemy mogli obejrzeć, a potem skopiować interesującą nas nazwę funkcji i zastosować ją jako alias. Tak zadeklarowana funkcja na pewno powinna już działać bez problemu.

Musimy pamiętać także o zachowaniu kompatybilności z VB, bowiem niektóre funkcje mogą nie dać się użyć.

Marcin Porębski ( Doogie )

marcin.porebski@interia.pl